package com.learn.CAS;

import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicStampedReference;

/**
 * ABA 问题通过stack对象体现
 */
public class AtomicReferenceABA {
    AtomicReference<Node> stack = new AtomicReference<>();
    AtomicStampedReference<Node> stampe = new AtomicStampedReference(new Node(1),1);

    //pop出栈
    public Node pop(){
        for (;;){
            Node top = stack.get();
            if(top==null){
                return null;
            }
            //CAS推出top元素
            if (stack.compareAndSet(top, top.next)) {
                top.next = null;
                return top;
            }
        }
    }

    //pop改造出栈
    public Node popByStamped(){
        for (;;){
            Node top = stampe.getReference();
            int stamp = stampe.getStamp();
            if(top==null){
                return null;
            }
            //CAS推出top元素
            if (stampe.compareAndSet(top, top.next,stamp,stamp+1)) {
                top.next = null;
                return top;
            }
        }
    }

    //push入栈
    public void push(Node node){
        for(;;){
            Node top = stack.get();
            if (stack.compareAndSet(top,node)) {
                node.next = top;
                return;
            }
        }
    }

    //push改造入栈
    public void pushByStamped(Node node){
        for(;;){
            Node top = stampe.getReference();
            int stamp = stampe.getStamp();
            if (stampe.compareAndSet(top,node,stamp,stamp+1)) {
                node.next = top;
                return;
            }
        }
    }

    static class Node{
        int value;
        Node next;
        public Node(int value) {
            this.value = value;
        }
    }

    public static void main(String[] args) {
        AtomicReferenceABA stack = new AtomicReferenceABA();
        Node n1 = new Node(3);
        Node n2 = new Node(2);
        Node n3 = new Node(1);
        stack.push(n1);
        stack.push(n2);
        stack.push(n3);

        //对线程A的pop对象打Thread断点发现pop出栈值是1，来源于线程B最后push进来的值 不等于  线程B第一次pop出去的值
        new Thread(()->{
            Node pop = stack.pop();
            System.out.println("Thread 1 pop : "+pop.value);
        },"A").start();

        new Thread(()->{
            Node a = stack.pop();
            System.out.println("Thread 2 pop : "+a.value);
            Node b = stack.pop();
            System.out.println("Thread 2 pop : "+b.value);
            stack.push(a);
        },"B").start();


        //使用AtomicStampedReference 带上版本号的乐观锁解决ABA问题
        AtomicReferenceABA stack1= new AtomicReferenceABA();
        stack1.pushByStamped(new Node(2));
        stack1.pushByStamped(new Node(3));

        new Thread(()->{
            Node pop = stack1.popByStamped();
            System.out.println("Thread 3 pop : "+pop.value + " stamp： "+stack1.stampe.getStamp());
        },"C").start();

        new Thread(()->{
            Node a = stack1.popByStamped();
            System.out.println("Thread 4 pop : "+a.value + " stamp： "+stack1.stampe.getStamp());
            Node b = stack1.popByStamped();
            System.out.println("Thread 4 pop : "+b.value + " stamp： "+stack1.stampe.getStamp());

            stack1.pushByStamped(a);
        },"D").start();
    }
}
